//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008-2010, Shane J. M. Liesegang
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions are met:
// 
//     * Redistributions of source code must retain the above copyright 
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright 
//       notice, this list of conditions and the following disclaimer in the 
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the copyright holder nor the names of any 
//       contributors may be used to endorse or promote products derived from 
//       this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
// POSSIBILITY OF SUCH DAMAGE.
//////////////////////////////////////////////////////////////////////////////

#pragma once

///A two-dimensional floating point vector and associated math functions
/** 
 * A floating point two-dimensional vector. Can be used either as a traditional
 *  vector (indicating a direction) or a position (treating X and Y as 
 *  coordinates). 
 */

struct Vector2
{
	/**
	 * The x dimension, publicly available because it's so often gotten or
	 * set and there's no good reason to encapsulate it.
	 */
	float X;

	/**
	 * The Y dimension, also publicly available.
	 */
	float Y;

	/**
	 * A reference to a zero-length vector (0, 0)
	 *
	 * NB: We can't make a static member variable constant, so it is
	 * possible to change this value, That is a horrible idea and will make
	 * the engine act "odd" at best.
	 */
	static Vector2 Zero;

	/**
	 * A reference to a (1, 1) vector
	 *
	 * NB: We can't make a static member variable constant, so it is
	 * possible to change this value. That is a horrible idea and will make
	 * the engine act "odd" at best.
	 */
	static Vector2 One;

	 /** 
         * A reference to a (1, 0) vector
         * 
         * NB: We can't make a static member variable constant, so it is 
         *  possible to change this value. That is a horrible idea and will make
         *  the engine act "odd" at best.
         */
        static Vector2 UnitX;
        
        /** 
         * A reference to a (0, 1) vector
         * 
         * NB: We can't make a static member variable constant, so it is 
         *  possible to change this value. That is a horrible idea and will make
         *  the engine act "odd" at best.
         */
        static Vector2 UnitY;
        
        /**
         * Constructor to initialize the vector to set dimensions
         * 
         * @param x The X dimension
         * @param y The Y dimension
         */
        Vector2(float x, float y);
        
        /**
         * Constructor to initialize the vector to uniform dimensions
         * 
         * @param value The value to use for both the X and Y dimension
         */
        Vector2(float value);
        
        /**
         * Constructor to initialize a zero-length vector (0, 0)
         */
        Vector2();
        
        
        /**
         * Get the absolute magnitude of the vector. Uses a square-root, so be 
         *  careful calling this too much within a loop.
         * 
         * @return The length (magnitude) of the vector
         */
        float Length();
        
        /**
         * Get the squared magnitude of the vector -- if all you care about is
         *  comparison, it's a lot faster to consider the squared lengths of 
         *  the vectors.
         * 
         * @return The length (magnitude) of the vector squared
         */
        float LengthSquared();

        /**
         * Get the absolute distance between two points (most useful if the
         *  Vector2 represents a position). Uses a square-root, so be careful
         *  calling this too much within a loop.
         * 
         * @param value1 The first point
         * @param value2 The second point
         * @return The distance between the two points
         */
        static float Distance(const Vector2& value1, const Vector2& value2);
        
        /**
         * Get the absolute distance between two points -- if all you care about
         *  is comparison, it's a lot faster to consider the squared lengths of
         *  the vectors.
         * 
         * @param value1 The first point
         * @param value2 The second point
         * @return The distance between the two points squared
         */
        static float DistanceSquared(const Vector2& value1, const Vector2& value2);
        
        /**
         * Get the dot product of two vectors.
         * 
         * @param value1 The first vector
         * @param value2 The second vector
         * @return The dot product
         */
        static float Dot(const Vector2& value1, const Vector2& value2);
        
        /**
         * Normalizes a vector in place -- retains its direction, but ensures
         *  that its magnitude is equal to 1.0. 
         */
        void Normalize();
        
        /**
         * Get the normalized value for a Vector2 without affecting the original. 
         * 
         * @param value The Vector2 to normalize
         * @return A normalized version of the passed-in Vector2
         */
        static Vector2 Normalize(const Vector2& value);
        
        /**
         * Reflect one Vector around another
         * 
         * @param vector The vector to reflect
         * @param normal The normal to reflect it around
         * @return The new vector resulting from the reflection
         */
        static Vector2 Reflect(const Vector2& vector, const Vector2& normal);
        
        /**
         * Get a new vector from the minimum X and minimum Y of the two
         * 
         * @return The vector composed from minimums on both axes
         */
        static Vector2 Min(const Vector2& value1, const Vector2& value2);
        
        /**
         * Get a new vector from the maximum X and maximum Y of the two
         * 
         * @return The vector composed from the maximums on both axes
         */
        static Vector2 Max(const Vector2& value1, const Vector2& value2);
        
        /**
         * Clamp a vector to a given minimum and maximum
         * 
         * @param value The vector to be clamped
         * @param min The vector representing the X and Y minimums for the clamping
         * @param max The vector representing the X and Y maximums for the clamping
         * @return The clamped vector
         */
        static Vector2 Clamp(const Vector2& value, const Vector2& min, const Vector2& max);
        
        /**
         * Perform a linear interpolation between two vectors
         * 
         * @param value1 The starting point vector
         * @param value2 The ending point vector
         * @param amount The amount (from 0.0 to 1.0) to interpolate between them
         * @return The interpolated vector
         */
        static Vector2 Lerp(const Vector2& value1, const Vector2& value2, float amount);
        
        /**
         * Get a negated vector -- if you add the result and the original, you
         *  should get a zero-length vector (0, 0)
         * 
         * @param value The vector to negate
         * @return The negated vector
         */
        static Vector2 Negate(const Vector2& value);
        
        /**
         * Rotate a vector
         * 
         * @param value The original vector to rotate
         * @param radians The rotation angle (in radians)
         * @return The rotated vector
         */
        static Vector2 Rotate(const Vector2& value, const float radians);
        
        bool operator==(const Vector2 &v) const;
        bool operator!=(const Vector2 &v) const;
        
        Vector2 operator-() const;
        Vector2 operator-(const Vector2 &v) const;
        Vector2 operator+(const Vector2 &v) const;
        Vector2 operator/(float divider) const;
        Vector2 operator*(float scaleFactor) const;

        Vector2& operator+=(const Vector2 &v);
        Vector2& operator-=(const Vector2 &v);
        Vector2& operator*=(float f);
        Vector2& operator/=(float f);
};
